package rolesystem.jade.server;

import java.io.Serializable;

/**
 * This class represents permissions of interactions between roles.
 */
public class PermissionMatrix implements Serializable
{
	/**
	 * Known roles.
	 */
	private String[] roles;

	/**
	 * Matrix of permissions associated to roles.
	 */
	private boolean[][] allowed;


	/**
	 * Constructs a default matrix, with no known roles.
	 * Interactions between unknown roles are allowed by default. 
	 */
	public PermissionMatrix()
	{
		roles=new String[1];
		allowed=new boolean[1][1];
		allowed[0][0]=true;
	}


	/**
	 * Copies this permission matrix from a specified one.
	 * @param original Permission matrix to copy.
	 */
	public synchronized void copy(PermissionMatrix original)
	{
		roles=original.roles;
		allowed=original.allowed;
	}


	/**
	 * Adds a new role to the known ones.
	 * Permissions related to the new role are copied from the ones related
	 * to unknown roles.
	 * @param role Role to add.
	 * @throws PMException If the specified role already exists.
	 */
	public synchronized void addRole(String role) throws PMException
	{
		String[] tmpRoles;
		boolean[][] tmpAllowed;
		int roleNumber=roles.length;
		int countS, countA;

		if(getIndex(role)!=0)
			throw new PMException();
		tmpRoles=new String[roleNumber+1];
		tmpAllowed=new boolean[roleNumber+1][roleNumber+1];
		for(countS=0; countS<roleNumber; countS++)
		{
			tmpRoles[countS]=roles[countS];
			for(countA=0; countA<roleNumber; countA++)
				tmpAllowed[countS][countA]=allowed[countS][countA];
			tmpAllowed[countS][roleNumber]=allowed[countS][0];
		}
		tmpRoles[roleNumber]=role;
		for(countA=0; countA<roleNumber; countA++)
			tmpAllowed[roleNumber][countA]=allowed[0][countA];
		tmpAllowed[roleNumber][roleNumber]=allowed[0][0];

		roles=tmpRoles;
		allowed=tmpAllowed;
	}


	/**
	 * Removes a role from the known ones.
	 * @param role Role to remove.
	 * @throws PMException If the specified role does not exist.
	 */
	public synchronized void removeRole(String role) throws PMException
	{
		String[] tmpRoles;
		boolean[][] tmpAllowed;
		int roleNumber=roles.length;
		int countS, countA, index=getIndex(role);

		if(index==0)
			throw new PMException();
		tmpRoles=new String[roleNumber-1];
		tmpAllowed=new boolean[roleNumber-1][roleNumber-1];
		for(countS=0; countS<index; countS++)
		{
			tmpRoles[countS]=roles[countS];
			for(countA=0; countA<index; countA++)
				tmpAllowed[countS][countA]=allowed[countS][countA];
			for(countA=index+1; countA<roleNumber; countA++)
				tmpAllowed[countS][countA-1]=allowed[countS][countA];
		}
		for(countS=index+1; countS<roleNumber; countS++)
		{
			tmpRoles[countS-1]=roles[countS];
			for(countA=0; countA<index; countA++)
				tmpAllowed[countS-1][countA]=allowed[countS][countA];
			for(countA=index+1; countA<roleNumber; countA++)
				tmpAllowed[countS-1][countA-1]=allowed[countS][countA];
		}

		roles=tmpRoles;
		allowed=tmpAllowed;
	}


	/**
	 * Checks whether interactions between the specified roles are
	 * allowed.
	 * @param senderRole Role of the sender.
	 * @param addresseeRole Role of the addressee.
	 * @return true if interactions are allowed, false otherwise.
	 */
	public synchronized boolean checkPermission(String senderRole,
		String addresseeRole)
	{
		return allowed[getIndex(senderRole)][getIndex(addresseeRole)];
	}


	/**
	 * Sets permission for interactions between the specified
	 * roles.
	 * @param senderRole Role of the sender; null indicates all
	 * unknown roles.
	 * @param addresseeRole Role of the addressee; null indicates
	 * all unknown roles.
	 * @param allow true if interactions must be allowed, false
	 * otherwise.
	 * @throws PMException If one or both of specified roles is not known.
	 */
	public synchronized void setPermission(String senderRole,
		String addresseeRole, boolean allow) throws PMException
	{
		int sender=getIndex(senderRole);
		int addressee=getIndex(addresseeRole);
		
		if( (sender==0 && senderRole!=null) ||
			(addressee==0 && addresseeRole!=null) )
			throw new PMException();
		allowed[sender][addressee]=allow;
	}


	/**
	 * Gets an array filled with all known roles.
	 * @return An array filled with all known roles.
	 */
	public synchronized String[] getKnownRoles()
	{
		String[] tmpRoles;
		int count, roleNumber=roles.length;

		tmpRoles=new String[roleNumber-1];
		for(count=1; count<roleNumber; count++)
			tmpRoles[count-1]=roles[count];
		return tmpRoles;
	}


	/**
	 * Returns the index associated to the specified role.
	 * @param role Role to search.
	 * @return The index associated to the specified role; this is
	 * 0 if role is null or the specified role is not known.
	 */
	private int getIndex(String role)
	{
		int count, roleNumber=roles.length;

		for(count=1; count<roleNumber; count++)
			if(roles[count].equals(role))
				return count;
		return 0;		
	}


	/**
	 * Exception of this class are thrown by methods of class
	 * PermissionMatrix.
	 */
	public static class PMException extends Exception
	{
	}
}
